#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>
#include<ctime>


using namespace std;

template<class T>
class BlockQueue
{
    static const int defaultnum = 5;
public:
    BlockQueue(int maxcap = defaultnum)
    :_maxcap(maxcap),
    {
        pthread_mutex_init(&_mutex, nullptr); 
        pthread_cond_init(&_ccond, nullptr);
        pthread_cond_init(&_pcond, nullptr);
        _lowwater = _maxcap/3;
        _highwater = _maxcap*2/3;
    }

    const T& Pop()
    {
        pthread_mutex_lock(&_mutex);
        if(q.size() == 0)
            pthread_cond_wait(&_ccond, &_mutex);
        T out = _q.front();
        _q.pop();
        if(q.size() == _lowwater) pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_mutex);
        return out;
    }

    void Push(const T& in)
    {
        pthread_mutex_lock(&_mutex);
        if(_q.size() == _maxcap)
            pthread_cond_wait(&_ccond, &_mutex);
        _q.push(in);
        if(_q.size() == _highwater) pthread_cond_signal(&_ccond);
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex, nullptr); 
        pthread_condattr_destroy(&_ccond, nullptr);
        pthread_condattr_destroy(&_pcond, nullptr);
    }
private:
    queue<T> _q;
    int _maxcap;
    pthread_mutex_t _mutex;
    pthread_cond_t _ccond;
    pthread_cond_t _pcond;
    int _lowwater;
    int _highwater;
};